#include "Server.h"
#include <cstdio>
#include <dirent.h>


Server::Server()
{
    this->s_port = 12345;
    this->s_address = "127.0.0.1";
    this->s_server_path = ".";
    this->s_show_hidden = false;
    this->send_num = 0;
    this->recv_num = 0;
    memset(this->cl_options, 0, this->OPT_NUM * sizeof(int));
}

void Server::set_port(int new_port)
{
    this->s_port = new_port;
}

void Server::set_address(char* address)
{
    this->s_address = address;
}

void Server::set_path(char* path)
{
    this->s_server_path = path;
}

void Server::set_hidden(bool hidden)
{
    this->s_show_hidden = hidden;
}

char const* Server::get_address()
{
    return this->s_address;
}

int Server::get_port()
{
    return this->s_port;
}

void Server::show()
{
    printf("fserver port:%d\n", this->s_port);
    printf("fserver address:%s\n", this->s_address);
    printf("fserver path:%s\n", this->s_server_path);
}

void Server::get_path_files() {
    DIR *pDir;
    struct dirent *ptr;
    this->s_files.clear();
    if (!(pDir = opendir(this->s_server_path))) {
        printf("The path \"%s\" is invalid!", this->s_server_path);
        return;
    }
    while ((ptr = readdir(pDir)) != 0) {
        if (strcmp(ptr->d_name, ".") != 0 && strcmp(ptr->d_name, "..") != 0) {
            this->s_files.push_back(ptr->d_name);
        }
    }
    closedir(pDir);
}

void Server::print_path_files()
{
    get_path_files();
    printf("file lists:\n");
    if(this->s_show_hidden)
    {
        for (auto iter = s_files.cbegin(); iter != this->s_files.cend(); ++iter) {
            printf("%s\n", (*iter).c_str());
        }
    }
    else
    {
        for (auto iter = s_files.cbegin(); iter != this->s_files.cend(); ++iter) {
            char temp((*iter)[0]);
            if(temp != '.') printf("%s\n", (*iter).c_str());
        }
    }
}

int Server::create_socket()
{
    this->listenfd = socket(AF_INET, SOCK_STREAM, 0);
    return this->listenfd;
}

void Server::init_address()
{
    memset(&this->s_server_address, 0, sizeof(this->s_server_address));
    this->s_server_address.sin_family = AF_INET;
    this->s_server_address.sin_addr.s_addr = inet_addr(this->s_address);
    this->s_server_address.sin_port = htons(this->s_port);
}

int Server::bind_socket()
{
    // set the port reusable
    setsockopt(this->listenfd, SOL_SOCKET, SO_REUSEADDR, &this->contain, sizeof(int));
    int res = bind(this->listenfd, (struct sockaddr*)&(this->s_server_address), sizeof(this->s_server_address));
    return res;
}

int Server::listen_socket()
{
    int res = listen(this->listenfd, 100);
    return res;
}

int Server::accept_client(struct sockaddr_in client_addr, socklen_t size)
{
    // printf("wait to accpet connection...\n");
    this->connfd = accept(this->listenfd, (struct sockaddr*)&client_addr, &size);
    printf("Client %s connect to the server\n", inet_ntoa(client_addr.sin_addr));
    return this->connfd;
}

void Server::close_listenfd()
{
    close(this->listenfd);
}

void Server::close_connfd()
{
    close(this->connfd);
}

int Server::read_buffer()
{
    // printf("reading buffer from client\n");
    this->recv_num = read(this->connfd, this->recv_buff, MAXLINE);
    return this->recv_num;
}

void Server::flush_buffer()
{
    memset(this->recv_buff, '\0', MAXLINE);
    memset(this->send_buff, '\0', MAXLINE);
    memset(this->cl_options, 0, OPT_NUM * sizeof(int));
    this->send_num = 0;
    this->recv_num = 0;
}

void Server::get_options()
{
    if(this->recv_buff[0] == 'l')
    {
        this->cl_options[OPT_l] = 1;
    }
    else if(this->recv_buff[0] == 'd')
    {
        this->cl_options[OPT_d] = 1;
    }
}

void Server::handle_options()
{
    get_options();
    if(this->cl_options[OPT_l])
    {
        this->send_file_lists();
    }
    else if(this->cl_options[OPT_d])
    {
        this->send_file();
    }
}

void Server::send_file()
{
    char file_name[this->recv_num - 1];
    // get the file name
    for(int i = 1; recv_buff[i] != '\0'; i++)
    {
        file_name[i - 1] = this->recv_buff[i];
    }
    file_name[this->recv_num - 1] = '\0';

    // check if the file name exists
    if(access(file_name, F_OK))
    {
        printf("failed to send file, as file %s doesn't exist\n", file_name);
        std::string error(" failed to send file, as file doesn't exist\n");
        for(int i = 0; i < error.length(); i++)
        {
            this->send_buff[i] = error[i];
        }
        this->send_num = error.length();
    }
    else
    {
        char* buffer;
        buffer = this->read_file(file_name, this->send_num);
        for(int i = 0; i < this->send_num; i++)
        {
            this->send_buff[i] = buffer[i];
        }
    }
    this->send_buff[this->send_num] = '\0';
    send_to_client();

    // if(!access(file_name, F_OK))
    // {
    //     printf("file name can be found\n");
    //     exit(0);
    // }
    // printf("file name can't be found\n");
    // exit(0);

    // if((fp = fopen(file_name, "r")) == NULL)
    // {
    //     printf("failed to send file, as file %s doesn't exist\n", file_name);
    //     std::string error(" failed to send file, as file doesn't exist\n");
    //     for(int i = 0; i < error.length(); i++)
    //     {
    //         this->send_buff[i] = error[i];
    //     }
    //     this->send_num = error.length();
    // }
    // else
    // {
    //     this->send_num = fread(this->send_buff, sizeof(this->send_buff), 1, fp);
    // }
}

void Server::send_file_lists()
{
    get_path_files();
    std::string file_lists("file_lists:\n");
    for(auto iter = s_files.cbegin(); iter != this->s_files.cend(); ++iter) {
        file_lists.append((*iter) + "\n");
    }
    write_send_buffer(file_lists);
    send_to_client();
}

void Server::send_to_client()
{
    int n;
    size_t nbytes = this->send_num * sizeof(char);
    if(n = send(this->connfd, this->send_buff, nbytes, 0) != nbytes)
    {
        printf("send %d bytes but it actually has %d bytes\n", n, (int)nbytes);
    }
}

void Server::write_send_buffer(std::string content)
{
    for(int i = 0; i < content.length(); i++)
    {
        this->send_buff[i] = content[i];
    }
    this->send_num = content.length();
    this->send_buff[this->send_num] = '\0';
}

void Server::show_listening()
{
    if(this->s_address == "127.0.0.1")
        printf("server listening at localhost:%d\n", this->s_port);
    else
        printf("server listening at %s:%d\n", this->s_address, this->s_port);
}

void Server::print_help()
{
    printf("Usage: fserver [OPTION] ... <DIR>\n");
    printf("serve some files over tcp \n\n");
    printf("args:\n");
    printf("    <DIR> Which directory to serve\n\n");
    printf("options:\n");
    printf("    -l, --listen=ADDR       specify source address to use [default is localhost]\n");
    printf("    -H, --hidden             show hidden file\n");
    printf("    -p, --port=PORT         specify listening port [default port is 12345]\n");
    printf("    -h, --help                display this help and exit\n");
}

char* Server::read_file(char* file_name, int& num)
{
	std::ifstream file(file_name);
	char* content;
	content = (char*)malloc(sizeof(char) * MAXLINE);
	if (content)
	{
		char buff[1024];
		int pt = 0;
		while (file.getline(buff, 1024))
		{
			for (int i = 0; i < 1024; i++) {
				char tmp = buff[i];
				if (tmp == '\0') {
					content[pt] = '\n';
					pt++;
					break;
				}
				content[pt] = tmp;
				pt++;
			}
		}
		content[pt] = '\0';
		char* result = (char*)malloc(sizeof(char) * (++pt));
		if (!result)
			return NULL;
		for (int i = 0; i < pt; i++) {
			result[i] = content[i];
		}
        num = pt;
		return result;
	}
    num = 0;
	return NULL;
}